Skip to content

Race-day audio on every page + TTS cold-start warm-up/watchdog#371

Merged
ryan-johnson2 merged 1 commit into
develfrom
global-callouts
Jul 4, 2026
Merged

Race-day audio on every page + TTS cold-start warm-up/watchdog#371
ryan-johnson2 merged 1 commit into
develfrom
global-callouts

Conversation

@ryan-johnson2

Copy link
Copy Markdown
Contributor

Two field reports: callouts stopped when leaving the Live page (the audio stack lived in that screen), and ~13s of race passed before the first tone/callout.

  • Global audio: the tones + callouts controller now mounts once in the app shell and follows the live stream on every page. The Live page keeps its Callouts toggle, driving the shared instance. Navigating back to Live mid-race can no longer re-fire the start tone (no remount).
  • Latency, measured: the data pipeline delivers passes to the live projection in 0.22–0.50s (median 0.33s — instrumented live on the rig). The stall was the browser's TTS engine cold-start silently swallowing the first utterance while the serialized queue waited behind it forever. Fixed with a volume-0 warm-up on the first user gesture and a 6s dead-utterance watchdog that cancels + advances the queue.

624 rd-console tests green (audio tests now mount the controller like the app shell does; new watchdog/warm-up unit tests), both svelte-checks clean, eslint+prettier clean.

🤖 Generated with Claude Code

…tart fixes

Field reports, same session:
1. Callouts only played on the Live Race Control page — the whole audio stack
   (player, speech queue, start/end tones, lap detection) lived in that screen
   and died on navigation. It now lives in an app-shell controller
   (raceDayAudio.svelte.ts) mounted once from App.svelte, consuming the same
   app-wide live stream the global header uses: a running race is audible from
   marshaling, rounds, results — anywhere. The Live page keeps the Callouts
   toggle + control-click unlocks, driving the shared instance. Bonus: page
   navigation no longer remounts the detectors, so returning to Live mid-race
   can never re-buzz the start tone.
2. ~13s of race with no tones or callouts. MEASURED the pipeline first: a
   scratch heat instrumented at 200ms poll showed passes visible in the live
   projection 0.22-0.50s (median 0.33s) after the crossing — the data path is
   sub-half-second. The stall is the browser TTS engine: its first-ever
   utterance can take seconds or die SILENTLY while the backend spins up, and
   the serialized queue dammed every later callout behind the corpse. Two
   fixes: warmUp() speaks a volume-0 utterance on the RD's first gesture (the
   same one-time unlock that resumes the AudioContext), and a per-utterance
   dead-engine WATCHDOG (6s) that cancels + advances so one swallowed utterance
   can never silence the rest of the race.

Noted for a later slice: adapter-appended passes carry no recorded_at (the
latency measurement had to poll live instead of reading the log).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@ryan-johnson2 ryan-johnson2 merged commit f27e94b into devel Jul 4, 2026
3 checks passed
@ryan-johnson2 ryan-johnson2 deleted the global-callouts branch July 4, 2026 23:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant